package org.six11.olive3.picturephone;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;

import javax.swing.*;

import org.six11.olive3.client.ErrorDialog;
import org.six11.olive3.client.OliveState;
import org.six11.olive3.net.Message;
import org.six11.olive3.net.MessageHandler;
import org.six11.olive3.net.MessageNames;
import org.six11.olive3.net.OliveNetworkTransmitter;
import org.six11.olive3.net.Message.Statement;
import org.six11.olive3.picturephone.server.PPMessageNames;
import org.six11.olive3.util.MultiState;
import org.six11.util2.Debug;

/**
 * 
 * 
 * @author Gabe Johnson <johnsogg@cmu.edu>
 * 
 */
public class PicturephoneUI extends JPanel {

  public static final String DONE = "whack me when done";
  private OliveNetworkTransmitter xmit;
  private MultiState state;
  private PicturephoneConsole console;
  private PicturephoneDrawingPane drawingPane;
  private JPanel cards;
  private Random entropy = new Random(System.currentTimeMillis());

  public PicturephoneUI(OliveNetworkTransmitter xmit, MultiState state) {
    this.xmit = xmit;
    this.state = state;
    state.setValue(DONE, "whack me");
    state.setValue(ProgramStateConstants.K_PLAY_MODE_PREF_RATE, true);
    state.setValue(ProgramStateConstants.K_PLAY_MODE_PREF_DESCRIBE, true);
    state.setValue(ProgramStateConstants.K_PLAY_MODE_PREF_SKETCH, true);
    state.addChangeListener(DONE, new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
        play();
      }
    });
    cards = new JPanel();
    cards.setLayout(new CardLayout());
    drawingPane = initDrawMode();
    cards.add(drawingPane, "Draw");
    cards.add(initDescribeMode(), "Describe");
    cards.add(initRateMode(), "Rate");
    setLayout(new BorderLayout());
    add(cards, BorderLayout.CENTER);

    // create a debugging console and means to access it.
    console = new PicturephoneConsole(xmit, state);
    Action popup = new AbstractAction() {
      public void actionPerformed(ActionEvent e) {
        setConsoleVisible(true);
      }
    };
    cards.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke('`'), Actions.CONSOLE);
    cards.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke('`'),
        Actions.CONSOLE);
    cards.getActionMap().put(Actions.CONSOLE, popup);

    // the following is purely for debugging. Picturephone doesn't have 'chat' per se.
    xmit.getStreamHandler().registerAsynchronousHandler(PPMessageNames.MSG_CHAT,
        new ChatMessageHandler(console));

    // say hello to the server so it initializes a few things.
    Message m = new Message(PPMessageNames.MSG_INIT);
    m.addParam(MessageNames.PARAM_USER_ID, state.getString(MessageNames.PARAM_USER_ID));
    xmit.addBodilessMessage(m);
    bug("Sending init!");

    initMessageHandlers();
    play();
  }

  public void play() {
    // randomly pick a phase to play and initiate that mode.
    Message m;
    // TODO: most likely I will remove the switch statement and just use the Anything handler. But
    // for now I'll leave this in here even though it won't be executed.
    int which = 3;
    switch (which) {
      case 0:
        // request a description to sketch.
        m = new Message(PPMessageNames.MSG_DESC_REQ);
        m.addParam(MessageNames.PARAM_USER_ID, state.getString(MessageNames.PARAM_USER_ID));
        xmit.addBodilessMessage(m);
        bug("-> Requesting... description!");
        break;
      case 1:
        // request a sketch to describe
        m = new Message(PPMessageNames.MSG_SKETCH_REQ);
        m.addParam(MessageNames.PARAM_USER_ID, state.getString(MessageNames.PARAM_USER_ID));
        xmit.addBodilessMessage(m);
        bug("-> Requesting... sketch!");
        break;
      case 2:
        // request the judgement interface with two sketches.
        m = new Message(PPMessageNames.MSG_RATE_REQ);
        m.addParam(MessageNames.PARAM_USER_ID, state.getString(MessageNames.PARAM_USER_ID));
        xmit.addBodilessMessage(m);
        bug("-> Requesting... judgement!");
        break;
      case 3:
        m = new Message(PPMessageNames.MSG_ANYTHING_REQ);
        m.addParam(MessageNames.PARAM_USER_ID, state.getString(MessageNames.PARAM_USER_ID));
        m.addParam(PPMessageNames.PARAM_PREF_DESC, state
            .getString(ProgramStateConstants.K_PLAY_MODE_PREF_DESCRIBE));
        m.addParam(PPMessageNames.PARAM_PREF_SKETCH, state
            .getString(ProgramStateConstants.K_PLAY_MODE_PREF_SKETCH));
        m.addParam(PPMessageNames.PARAM_PREF_RATE, state
            .getString(ProgramStateConstants.K_PLAY_MODE_PREF_RATE));
        xmit.addBodilessMessage(m);
        bug("-> Requesting... anything!");
    }
  }

  private void setConsoleVisible(boolean vis) {
    console.setVisible(vis);
  }

  @SuppressWarnings("unused")
  private void bug(String what) {
    Debug.out("PicturephoneDrawingPane", what);
  }

  private void showCard(String name) {
    CardLayout cardLayout = (CardLayout) cards.getLayout();
    cardLayout.show(cards, name);
  }

  private void initMessageHandlers() {
    xmit.getStreamHandler().registerAsynchronousHandler(PPMessageNames.MSG_DESC_RES,
        new AWTMessageHandler() {
          public void awtHandleLater(Message m) {
            showCard("Draw");

            // TODO: this should be done in the describe ui class w/ a separately registered handler
            String msg = m.getParamValue(PPMessageNames.PARAM_DESC_TEXT);
            String descID = m.getParamValue(PPMessageNames.PARAM_DESC_ID);
            drawingPane.setDescription(msg, descID);
            state.setValue(ProgramStateConstants.K_PLAY_MODE,
                ProgramStateConstants.V_PLAY_MODE_SKETCH);
          }
        });

    xmit.getStreamHandler().registerAsynchronousHandler(PPMessageNames.MSG_PEN,
        new AWTMessageHandler() {
          public void awtHandleLater(Message m) {
            showCard("Describe");
            state.setValue(ProgramStateConstants.K_PLAY_MODE,
                ProgramStateConstants.V_PLAY_MODE_DESCRIBE);
          }
        });

    xmit.getStreamHandler().registerAsynchronousHandler(PPMessageNames.MSG_RATE_RES,
        new AWTMessageHandler() {
          public void awtHandleLater(Message m) {
            showCard("Rate");
            state.setValue(ProgramStateConstants.K_PLAY_MODE,
                ProgramStateConstants.V_PLAY_MODE_RATE);
          }
        });

    xmit.getStreamHandler().registerAsynchronousHandler(PPMessageNames.MSG_ERROR,
        new AWTMessageHandler() {
          public void awtHandleLater(Message m) {
            ErrorDialog.showError("Picturephone Message", m
                .getParamValue(PPMessageNames.PARAM_TEXT));
          }
        });

    xmit.getStreamHandler().registerAsynchronousHandler(PPMessageNames.MSG_STATUS,
        new AWTMessageHandler() {
          public void awtHandleLater(Message m) {
            for (Statement s : m.statements) {
              if (s.type.equals(PPMessageNames.STMT_SCORE)) {
                state.setValue(ProgramStateConstants.K_SCORE, s.required.get(0).text);
                String why = "";
                if (s.getParamValue(PPMessageNames.PARAM_TEXT) != null) {
                  why = s.getParamValue(PPMessageNames.PARAM_TEXT);
                }
                state.setValue(ProgramStateConstants.K_SCORE_REASON, why);
              }
            }
          }
        });
  }

  private PicturephoneDrawingPane initDrawMode() {
    return new PicturephoneDrawingPane(xmit, state);
  }

  private JPanel initDescribeMode() {
    return new PicturephoneDescriptionPane(xmit, state);
  }

  private JPanel initRateMode() {
    return new PicturephoneRatePane(xmit, state);
  }
  // AC/DC is perhaps the awesomest horrible band ever.
}